package core.validate;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class NumberRule extends Rule<NumberRule> {
    public NumberRule(String key) {
        super(key);
        actions.add(holder -> {
            Object value = holder.getValue();
            String strVal = value.toString();
            Number numVal = null;
            if(strVal != null && strVal.matches("^-?\\d+(\\.\\d+)?$")){
                if(strVal.contains(".")){
                    numVal = Double.parseDouble(strVal);
                }else{
                    numVal = Integer.parseInt(strVal);
                }
            }
            holder.setStatus(true).setValue(numVal);
        });
    }

    protected NumberRule inst() {
        return this;
    }

    public NumberRule type(String type, String message) {
        actions.add(holder -> {
            boolean status = false;
            Object value = holder.getValue();
            if(value != null){
                String str = null;
                try{
                    str = holder.getValue().toString();
                }catch (Exception ignored){}
                if(str != null) {
                    String pattern = null;
                    Number num = null;

                    switch (type){
                        case "long", "int" -> pattern = "^-?\\d+$";
                        case "float", "double" -> pattern = "^-?\\d+(\\.\\d+)?$";
                    }
                    if (pattern != null) {
                        Matcher matcher = Pattern.compile(pattern).matcher(str);
                        status = matcher.matches();
                        if(status){
                            switch (type){
                                case"long" -> num = Long.parseLong(str);
                                case"int" -> num = Integer.parseInt(str);
                                case"float" -> num = Float.parseFloat(str);
                                case"double" -> num = Double.parseDouble(str);
                            }
                        }
                    }

                    holder.setValue(num);
                }
            }
            holder.setStatus(status).setMessage(getMessage(message));
        });
        return this;
    }

    public NumberRule compare(String operator, Number target, String message){
        actions.add(holder -> {
            Number value = holder.getValue();
            boolean status = true;
            if(value == null) {
                value = 0;
            }
            switch (operator) {
                case ">" -> status = value.doubleValue() > target.doubleValue();
                case "<" -> status = value.doubleValue() < target.doubleValue();
                case ">=" -> status = value.doubleValue() >= target.doubleValue();
                case "<=" -> status = value.doubleValue() <= target.doubleValue();
                case "==" -> status = value.doubleValue() == target.doubleValue();
                case "!=" -> status = value.doubleValue() != target.doubleValue();
            }
            holder.setStatus(status).setMessage(getMessage(message));
        });
        return this;
    }

    public NumberRule max(Number max, String message) {
        return compare("<=", max, message);
    }

    public NumberRule min(Number min, String message) {
        return compare(">=", min, message);
    }

    public NumberRule between(Number min, Number max, String message){
        actions.add(holder -> {
            Number value = holder.getValue();
            if(value == null) {
                value = 0;
            }
            holder.setStatus(
                    value.doubleValue() >= min.doubleValue()
                    && value.doubleValue() <= max.doubleValue()
            ).setMessage(getMessage(message));
        });
        return this;
    }

    public NumberRule notBetween(Number min, Number max, String message){
        actions.add(holder -> {
            Number value = holder.getValue();
            if(value == null) {
                value = 0;
            }
            holder.setStatus(
                    value.doubleValue() < min.doubleValue()
                    || value.doubleValue() > max.doubleValue()
            ).setMessage(getMessage(message));
        });
        return this;
    }

    public NumberRule multiple (Number target, String message){
        actions.add(holder -> {
            Number value = holder.getValue();
            if(value == null) {
                value = 0;
            }
            holder.setStatus(
                    value.doubleValue() % target.doubleValue() == 0
            ).setMessage(getMessage(message));
        });
        return this;
    }

    public NumberRule gtWith(Number target, String message){
        return compare(">", target, message);
    }

    public NumberRule gteWith(Number target, String message){
        return compare(">=", target, message);
    }

    public NumberRule ltWith(Number target, String message){
        return compare("<", target, message);
    }

    public NumberRule lteWith(Number target, String message){
        return compare("<=", target, message);
    }

    public NumberRule eqWith(Number target, String message){
        return compare("==", target, message);
    }

    public NumberRule neqWith(Number target, String message){
        return compare("!=", target, message);
    }
}